#pragma once
#include<queue>
#include<iostream>
#include<pthread.h>

using namespace std;

const int max_cap = 5;
template<class T>
class BlockQueue
{
public:
  BlockQueue(int cap = max_cap) 
    :_cap(cap)
  {
    pthread_mutex_init(&_mtx,nullptr);
    pthread_cond_init(&_empty,nullptr);
    pthread_cond_init(&_full,nullptr);
  }
  ~BlockQueue()
  {
    pthread_mutex_destroy(&_mtx);
    pthread_cond_destroy(&_full);
    pthread_cond_destroy(&_empty);
  }
  void push(const T& val)
  {
    pthread_mutex_lock(&_mtx);
    while(isFull())
    {
      pthread_cond_wait(&_full,&_mtx);
    }
    _bq.push(val);
    if(_bq.size() > _cap/2)
      pthread_cond_signal(&_empty);
    pthread_mutex_unlock(&_mtx);
  }
  void pop(T* data)
  {
    pthread_mutex_lock(&_mtx);
    while(isEmpty())
    {
      pthread_cond_wait(&_empty,&_mtx);
    }
    *data = _bq.front();
    _bq.pop();
    if(_bq.size() < _cap / 2)
      pthread_cond_signal(&_full);
    pthread_mutex_unlock(&_mtx);
  }
  bool isEmpty()
  {
    return _bq.size() == 0;
  }
  bool isFull()
  {
    return _bq.size() == _cap;
  }
private:
  queue<T> _bq;
  pthread_mutex_t _mtx;
  pthread_cond_t _empty;
  pthread_cond_t _full;
  int _cap;
};
